import ReactCurrentBatchConfig from '../../react/src/currentBatchConfig';
import {
	unstable_getCurrentPriorityLevel,
	unstable_IdlePriority,
	unstable_ImmediatePriority,
	unstable_NormalPriority,
	unstable_UserBlockingPriority
} from 'scheduler';
import { FiberRootNode } from './fiber';

export type Lane = number; // 优先级
export type Lanes = number; // 优先级 集合

// 用二进制 更灵活
// 可以选出 一个优先级更高的 去 调度
// 也可以选出一批 优先级 高的去调度  // 并发更新的时候要选出一批去调度

// 默认规则 lane越小 优先级越高 0 除外
export const SyncLane = 0b00001; // 同步优先级的 lane  在react 中代表 用微任务调度
//  对应 scheduler 中的优先级为1 _ImmediatePriority 但在 scheduler 是用宏任务调度

export const NoLane = 0b00000; // 没有优先级的 lane

export const NoLanes = 0b00000; // 没有优先级的 集合

export const InputContinuousLane = 0b00010;
// 连续的输入 也就是 拖拽等的优先级 宏任务调度 并发的 对应调度器优先级  2 UserBlockingPriority 拖拽 滚动等

export const DefaultLane = 0b00100; // 默认的优先级 对应3

export const TransitionLane = 0b01000;

export const IdleLane = 0b10000; // 空闲的优先级 对应 调度器优先级中的 5

// 将 优先级 合并 如果是 0b01000 ｜ 0b00010 就变成了  0b01010
export function mergeLanes(laneA: Lane, laneB: Lane): Lanes {
	return laneA | laneB;
}

// 根据不同情况生成一个 优先级lane 返回
// 所有触发更新的地方都需要获取lane
// 1.首屏渲染
// 2. dispatch setState 触发的更新
export function requestUpdateLane() {
	// 可以根据 不同情况 返回不同的优先级
	// 比如在在click 产生的更新 还有就是在 useEffect 函数中 产生的更新
	const isTransition = ReactCurrentBatchConfig.transition !== null;
	if (isTransition) {
		return TransitionLane;
	}

	// 从上下文环境中获取Scheduler优先级
	// 事件  回调执行之前 已经将他们对应的优先级 设置到了Scheduler 全局上下文中
	const currentSchedulerPriority = unstable_getCurrentPriorityLevel();

	// 将 调度器的优先级 和 lane 模型 一一对应 返回 对应的lane
	// 为什么调度器和 react要使用两种优先级 因为调度器是另一个项目可以给其他项目用 是解耦的
	const lane = schedulerPriorityToLane(currentSchedulerPriority);
	return lane;
}

// 返回 lanes 中优先级最高的lane 也就是 最靠右的lane
export function getHighestPriorityLane(lanes: Lanes): Lane {
	// lanes 0b00011 =》返回 0b00001
	// lanes 0b00110 =》返回 0b00010
	// 返回最靠右的那一位
	return lanes & -lanes;
}

// 取交集
// 将传入的 lanes 或lane 和subset lane取交集 再和 subset 比 相等则返回true
// 简单理解 就是判断两lane是否相等
// 还有一种可能 subset 为 NoLane 则返回 结果也会是 true
export function isSubsetOfLanes(set: Lanes, subset: Lane) {
	return (set & subset) === subset;
}

export function markRootFinished(root: FiberRootNode, lane: Lane) {
	root.pendingLanes &= ~lane; // 将lane 从pendingLanes 移除 0b00001 0b00001 =》0b00000；

	root.suspendedLanes = NoLanes;
	root.pingedLanes = NoLanes;
}

// 将 lanes 然后获取其中优先级最高的lane 然后将lane 优先级转换成 调度器的优先级
export function lanesToSchedulerPriority(lanes: Lanes) {
	const lane = getHighestPriorityLane(lanes);

	if (lane === SyncLane) {
		return unstable_ImmediatePriority;
	}
	if (lane === InputContinuousLane) {
		return unstable_UserBlockingPriority;
	}
	if (lane === DefaultLane) {
		return unstable_NormalPriority;
	}
	return unstable_IdlePriority;
}

// 从调度器优先级 转成lane优先级
export function schedulerPriorityToLane(schedulerPriority: number): Lane {
	if (schedulerPriority === unstable_ImmediatePriority) {
		return SyncLane;
	}
	if (schedulerPriority === unstable_UserBlockingPriority) {
		return InputContinuousLane;
	}
	if (schedulerPriority === unstable_NormalPriority) {
		return DefaultLane;
	}
	return NoLane;
}

export function markRootPinged(root: FiberRootNode, pingedLane: Lane) {
	root.pingedLanes |= root.suspendedLanes & pingedLane;
}

export function markRootSuspended(root: FiberRootNode, suspendedLane: Lane) {
	root.suspendedLanes |= suspendedLane;
	root.pingedLanes &= ~suspendedLane;
}

// 某种规则 返回 最合适 的lane
export function getNextLane(root: FiberRootNode): Lane {
	const pendingLanes = root.pendingLanes;

	if (pendingLanes === NoLanes) {
		return NoLane;
	}
	let nextLane = NoLane;

	// 排除掉挂起的lane
	const suspendedLanes = pendingLanes & ~root.suspendedLanes;

	if (suspendedLanes !== NoLanes) {
		// 返回优先级最高的 lane
		nextLane = getHighestPriorityLane(suspendedLanes);
	} else {
		const pingedLanes = pendingLanes & root.pingedLanes;
		if (pingedLanes !== NoLanes) {
			nextLane = getHighestPriorityLane(pingedLanes);
		}
	}
	return nextLane;
}

export function includeSomeLanes(set: Lanes, subset: Lane | Lanes): boolean {
	return (set & subset) !== NoLanes;
}

export function removeLanes(set: Lanes, subet: Lanes | Lane): Lanes {
	return set & ~subet;
}
